package cachex

import (
	"context"
	"time"

	"gitee.com/hasika/gotool/container"
)

type cacheItem[V any] struct {
	visitTime  time.Time
	expireTime time.Duration
	value      V
}

type AutoExtendCache[K comparable, V any] struct {
	data          container.MapInterface[K, *cacheItem[V]]
	ctx           context.Context
	cleanDuration time.Duration
}

func NewAutoExtendCache[K comparable, V any](ctx context.Context, cleanDuration time.Duration) *AutoExtendCache[K, V] {
	if cleanDuration == 0 {
		cleanDuration = time.Minute
	}
	ret := &AutoExtendCache[K, V]{data: container.NewSyncMap[K, *cacheItem[V]](), ctx: ctx, cleanDuration: cleanDuration}
	go ret.clean()
	return ret
}

func (c *AutoExtendCache[K, V]) Get(k K) (value V, load bool) {
	item, itemLoad := c.data.Load(k)
	if !itemLoad {
		return value, false
	}
	if item.visitTime.Add(item.expireTime).Before(time.Now()) {
		return value, false
	}
	item.visitTime = time.Now()
	return item.value, true
}

func (c *AutoExtendCache[K, V]) Set(k K, v V, expire time.Duration) {
	c.data.Store(k, &cacheItem[V]{value: v, visitTime: time.Now(), expireTime: expire})
}

func (c *AutoExtendCache[K, V]) clean() {
	ticker := time.NewTicker(c.cleanDuration)
	defer ticker.Stop()
	for {
		select {
		case c.cleanDuration = <-ticker.C:
			c.cleanOnce()
		case <-c.ctx.Done():
			return
		}
	}

}

func (c *AutoExtendCache[K, V]) cleanOnce() {
	now := time.Now()
	c.data.Range(func(key K, value *cacheItem[V]) (shouldContinue bool) {
		if value.visitTime.Add(value.expireTime).Before(now) {
			c.data.Delete(key)
		}
		return true
	})
}
